<script lang="ts">
    import {debounce} from "@/common/util";
    import mixin from "@/uni_modules/u-tools/mixin/mixin";
    import mpMixin from "@/uni_modules/u-tools/mixin/mpMixin";
    import {TransitionType} from "@/uni_modules/u-transition/components/u-transition/types";
    import props from "./props";

    // 当前需要做防抖处理的动画集合
    const transitionDebounceMap: TransitionType[] = ["fade-up", "fade-right", "fade-down", "fade-left", "fade-zoom"];

    export default {
        name: "u-transition",
        // 混入
        mixins: [props, mixin, mpMixin],
        // 事件
        emits: ["beforeEnter", "enter", "afterEnter", "beforeLeave", "leave", "afterLeave"],
        // 数据
        data()
        {
            return {
                // 是否完成初始化
                init: false,
                // 记录组件动画的状态
                status: "",
                // 组件是否展示
                display: false,
                // 应用的类名
                classes: "",
                // 过渡动画是否完毕
                done: true,
                // 防抖处理后的 transitionendHandler
                debouncedTransitionendHandler: () =>
                {
                }
            };
        },
        // 组件挂载
        mounted()
        {
            // 防抖处理transitionend事件
            this.debouncedTransitionendHandler = debounce((event: Record<string, any>) =>
            {
                // 过渡动画是否完毕，完毕后不再执行transitionend事件
                if (!this.done)
                {
                    // 设置过渡动画完毕
                    this.done = true;

                    // 清空应用类名
                    this.classes = "";

                    // 判断是否为退场动画，如果是，则隐藏组件
                    if (!this.show && this.display)
                    {
                        this.display = false;

                        this.init = false;
                    }

                    this.$emit(this.status === "enter" ? "afterEnter" : "afterLeave", {event});
                }

            }, transitionDebounceMap.includes(this.mode) ? this.debounceTime : 0);
        },
        // 监听器
        watch: {
            show: {
                handler(value: boolean)
                {
                    value ? this.enter() : this.leave();
                },
                immediate: true
            }
        },
        // 计算函数
        computed: {
            // 获取动画样式
            transitionStyle(): Record<string, any>
            {
                return {
                    transitionTimingFunction: this.timingFunction,
                    transitionDuration: `${this.duration}ms`
                };
            }
        },
        // 方法
        methods: {
            // 入场
            enter(): void
            {
                // 动画进入时的类名
                const classNames: Record<string, any> = this.getClassName(this.mode);

                // 设置过渡动画未完毕
                this.done = false;

                // 定义状态和发出动画进入前事件
                this.status = "enter";

                // 执行入场前回调
                this.$emit("beforeEnter");

                // 完成初始化
                this.init = true;

                // 动画开始时，显示组件
                this.display = true;

                // 定义应用的类名
                this.classes = classNames["enter"];

                // 在DOM更新后，执行入场事件和动画结束事件
                this.$nextTick(() =>
                {
                    // 这里不知道为什么要加上延迟执行动画才生效，不然动画会偶发性失败
                    asyncFn(() =>
                    {
                        // 入场事件
                        this.$emit("enter");

                        // 定义应用的类名
                        this.classes = classNames["enter-to"];

                    }, 20);
                });
            },
            // 退场
            leave(): void
            {
                if (!this.display)
                {
                    return;
                }

                // 设置过渡动画未完毕
                this.done = false;

                // 动画离场时的类名
                const classNames: Record<string, any> = this.getClassName(this.mode);

                // 定义状态和发出动画出场前事件
                this.status = "leave";

                // 执行离场前回调
                this.$emit("beforeLeave");

                // 定义应用的类名
                this.classes = classNames["leave"];

                // 在DOM更新后，执行离场事件和动画结束事件
                this.$nextTick(() =>
                {
                    // 这里不知道为什么要加上延迟执行动画才生效，不然动画会偶发性失败
                    asyncFn(() =>
                    {
                        // 离场事件
                        this.$emit("leave");

                        // 定义应用的类名
                        this.classes = classNames["leave-to"];

                    }, 20);
                });
            },
            // 定义类名
            getClassName(mode: TransitionType): Record<string, any>
            {
                return {
                    "enter": `u-${mode}-enter u-${mode}-enter-active`,
                    "enter-to": `u-${mode}-enter-to u-${mode}-enter-active`,
                    "leave": `u-${mode}-leave u-${mode}-leave-active`,
                    "leave-to": `u-${mode}-leave-to u-${mode}-leave-active`
                };
            }
        }
    };
</script>

<template>
    <!-- 动画组件 -->
    <view
        v-if="init"
        :class="classes"
        :style="[
            customStyle,
            transitionStyle
        ]"
        class="u-transition"
        @transitionend="debouncedTransitionendHandler"
    >
        <slot />
    </view>
    <!-- //动画组件 -->
</template>

<style lang="scss" scoped>
    /* 淡入 */
    @use "./fade.scss" as *;

    /* 缩放 */
    @use "./zoom.scss" as *;

    /* 缩放淡入 */
    @use "./fade-zoom.scss" as *;

    /* 上滑淡入 */
    @use "./fade-up.scss" as *;

    /* 左滑淡入 */
    @use "./fade-left.scss" as *;

    /* 右滑淡入 */
    @use "./fade-right.scss" as *;

    /* 下滑淡入 */
    @use "./fade-down.scss" as *;

    /* 上滑进入 */
    @use "./slide-up.scss" as *;

    /* 左滑进入 */
    @use "./slide-left.scss" as *;

    /* 右滑进入 */
    @use "./slide-right.scss" as *;

    /* 下滑进入 */
    @use "./slide-down.scss" as *;

</style>